package net.findbugs.demo.analysis;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.InstructionHandle;

import edu.umd.cs.findbugs.ba.BasicBlock;
import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
import edu.umd.cs.findbugs.ba.DepthFirstSearch;
import edu.umd.cs.findbugs.ba.Edge;
import edu.umd.cs.findbugs.ba.ForwardDataflowAnalysis;

public class LockCountAnalysis extends ForwardDataflowAnalysis<LockCount> {
	
	private boolean isSynchronized;
	
	public LockCountAnalysis(DepthFirstSearch dfs, boolean isSynchronized) {
		super(dfs);
		this.isSynchronized = isSynchronized;
	}

	@Override
	public boolean isFactValid(LockCount fact) {
		return !(fact.isTop() || fact.isBottom());
	}

	@Override
	public void transferInstruction(InstructionHandle handle,
	        BasicBlock basicBlock, LockCount fact)
	        throws DataflowAnalysisException {
		if (!isFactValid(fact)) {
			return;
		}
		
		short opcode = handle.getInstruction().getOpcode();
		if (opcode == Constants.MONITORENTER) {
			fact.setCount(fact.getCount() + 1);
		} else if (opcode == Constants.MONITOREXIT) {
			fact.setCount(fact.getCount() - 1);
		}
		
		if (fact.getCount() < 0) {
			fact.setCount(LockCount.BOTTOM);
		}
	}

	public void copy(LockCount source, LockCount dest) {
		dest.setCount(source.getCount());
	}

	public LockCount createFact() {
		return new LockCount(0);
	}

	public void initEntryFact(LockCount result)
	        throws DataflowAnalysisException {
		result.setCount(isSynchronized ? 1 : 0);
	}

	public boolean isTop(LockCount fact) {
		return fact.isTop();
	}

	public void makeFactTop(LockCount fact) {
		fact.setCount(LockCount.TOP);
	}

	public void meetInto(LockCount fact, Edge edge, LockCount result)
	        throws DataflowAnalysisException {
		if (fact.isTop() || result.isBottom()) {
			return;
		}
		if (fact.isBottom() || result.isTop()) {
			copy(fact, result);
			return;
		}
		if (fact.getCount() != result.getCount()) {
			result.setCount(LockCount.BOTTOM);
		}
	}

	public boolean same(LockCount fact1, LockCount fact2) {
		return fact1.getCount() == fact2.getCount();
	}

}
